修改 update_user:接收 AuthenticatedUser 並做 owner 檢查
use crate::extractors::AuthenticatedUser;
pub async fn update_user(
Extension(pool): Extension<PgPool>,
Extension(mut redis): Extension<MultiplexedConnection>,
AuthenticatedUser(claims): AuthenticatedUser, // <- 新增
Path(id): Path<i64>,
Json(payload): Json<UpdateUser>,
) -> Result<impl IntoResponse, AppError> {
// 1) 授權檢查:只有 owner 可以更新
let caller_id: i64 = claims.sub.parse().map_err(|_| (StatusCode::UNAUTHORIZED, "invalid token sub".to_string()))?;
if caller_id != id {
return Err((StatusCode::FORBIDDEN, "forbidden".to_string()));
}
// 原先更新邏輯(查 existing、hash password、UPDATE ...)
let existing = sqlx::query_as::<_, User>("SELECT id, username, email, password_hash, created_at, updated_at FROM users WHERE id = $1")
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|e| internal_err(e))?;
}
修改 delete_user:同樣加入 AuthenticatedUser 與授權檢查
pub async fn delete_user(
Extension(pool): Extension<PgPool>,
Extension(mut redis): Extension<MultiplexedConnection>,
AuthenticatedUser(claims): AuthenticatedUser, // <- 新增
Path(id): Path<i64>,
) -> Result<impl IntoResponse, AppError> {
let caller_id: i64 = claims.sub.parse().map_err(|_| (StatusCode::UNAUTHORIZED, "invalid token sub".to_string()))?;
if caller_id != id {
return Err((StatusCode::FORBIDDEN, "forbidden".to_string()));
}
let res = sqlx::query!(
r#"
DELETE FROM users
WHERE id = $1
"#,
id
)
.execute(&pool)
.await
.map_err(|e| internal_err(e))?;
}
另外的強化:把 owner 驗證放到 SQL
若擔心 race condition(TOCTOU),可以在 SQL 的 WHERE 同時帶上 caller_id,例如(DELETE ):
let res = sqlx::query!(
r#"
DELETE FROM users
WHERE id = $1 AND id = $2
"#,
id, caller_id
)
.execute(&pool)
.await
.map_err(|e| internal_err(e))?;